using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace FluentBinding.WinForms.Controls
{
    /// <typeparam name="E">Type of the root entity</typeparam>
    /// <typeparam name="P">Type of the property in the data-bound control</typeparam>
    public class ComboBoxSelectedItemBinding<E, P> : PropertyBinding<E, P>, IComboBoxSelectedItemBinding<E, P>
        where E : class
        where P : class
    {
        #region Fields

        private readonly ComboBox[] _controls;

        #endregion

        #region Constructors

        public ComboBoxSelectedItemBinding(BindingProxy<E> bindingProxy, ComboBox[] controls)
            : base(bindingProxy)
        {
            _controls = controls;
        }

        #endregion

        #region IComboBoxSelectedItemBinding<E,P> Members

        public IComboBoxSelectedItemBinding<E, P> To(Func<E, P> selector)
        {
            _bindingProxy.AssignDataMemberConvertor(
                new DataSourceEntityAdapterConvertor<E, P>(selector));

            return this;
        }

        public IComboBoxSelectedItemBinding<E, P> To(Func<E, P> selector, Action<E, P> initializer)
        {
            _bindingProxy.AssignDataMemberConvertor(
                new DataSourceEntityAdapterConvertor<E, P>(selector, initializer));

            return this;
        }

        public IComboBoxSelectedItemBinding<E, P> WithDataSource(
            Func<E, IEnumerable<P>> dataSource, Func<P, string> displayMember)
        {
            return WithDataSource(dataSource, displayMember, null);
        }

        public IComboBoxSelectedItemBinding<E, P> WithDataSource(Func<E, IEnumerable<P>> dataSource,
                                                                 Func<P, string> displayMember, string noSelectionText)
        {
            IList<DataSourceEntityAdapter<P>> adapters = dataSource(_bindingProxy.Entity)
                .Select(x => new DataSourceEntityAdapter<P>(x, displayMember))
                .ToList();

            if (noSelectionText != null)
                adapters.Insert(0, new DataSourceEntityAdapter<P>(null, x => noSelectionText));

            _controls.ForEach(x =>
            {
                x.DataSource = adapters;
                x.DisplayMember = DataSourceEntityAdapter<P>.DisplayMemberName;                                      
            });

            return this;
        }

        public IPropertyBinding<E, P> WithDataSourceUpdateMode(DataSourceUpdateMode updateMode)
        {
            _bindingProxy.DataSourceUpdateMode = updateMode;

            if (updateMode == DataSourceUpdateMode.OnPropertyChanged)
            {
                // ComboBox has a bug: it does not expose SelectedItemChanged property
                // as result DataBinder can not handle update in DataSourceUpdateMode.OnPropertyChanged
                // so we'll write value from control to entity manually
                _controls.ForEach(x => x.SelectedIndexChanged += (sender, args) =>
                {
                    _bindingProxy.Bindings.Single(b => b.Control == x).WriteValue();
                });
            }

            return this;
        }

        #endregion
    }
}